package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

/*
    小结：
        new this 具体的实例
        static Class 唯一的模板
 */
public class Test1 {
    public static void main(String[] args) {
        //Q1:无锁时，先发消息？先打电话？
        //Phone1 phone1 = new Phone1();
        //
        //new Thread(phone1::sendMessage, "A").start();
        //new Thread(phone1::call, "B").start();
        /*
            sendMessage
            call
         */


        //Q2: sychronized锁类中的方法，先发消息？先打电话？
        // 有锁，锁了对象的调用者 phone2 ,所以肯定先 发短信，再打电话，
        // 验证：发短信里面延迟4s,依然是先 发短信，再 打电话，因为 phone2 在4s内都被锁死了，B线程不能执行
        // 两个方法用的同一把锁。谁先拿到谁先执行
        //Phone2 phone2 = new Phone2();
        //new Thread(phone2::sendMessage,"A").start();
        //// 延迟1s
        //try {
        //    TimeUnit.SECONDS.sleep(1);
        //} catch (InterruptedException e) {
        //    e.printStackTrace();
        //}
        //new Thread(phone2::call,"B").start();
        /*
            发短信
            打电话
         */




        //Q3:发短信有锁sychronized，hello无锁  先发消息？先hello？
        //Phone2 phone2 = new Phone2();
        //new Thread(phone2::sendMessage,"A").start();
        //new Thread(phone2::hello,"B").start();
        /*
            hello
            发短信
         */



        //Q4:两个对象分别调用sychronized方法  哪个先发消息？
        //Phone2 phone21 = new Phone2();
        //Phone2 phone22 = new Phone2();
        //new Thread(()->{
        //    for (int i = 0; i < 10; i++) {
        //        phone21.sendMessage();
        //    }
        //},"A").start();
        //new Thread(()->{
        //    for (int i = 0; i < 10; i++) {
        //        phone22.sendMessage();
        //    }
        //},"B").start();
        /*
            ...
            发短信-A   没有规则顺序，因为调用的不是一个对象，所以锁没有什么作用
            发短信-B
            发短信-B
            发短信-A
            发短信-B
            ...
         */


        //Q5:静态synchronized方法，先发消息？先打电话？    坑：线程中间需要加睡眠，否则锁没有效果
        //Phone3 phone3 = new Phone3();
        //new Thread(()->{phone3.sendMessage();},"A").start();
        //new Thread(()->{phone3.call();},"B").start();
        /*
            发短信-A
            打电话-B
         */
        //Phone3 phone31 = new Phone3();
        //Phone3 phone32 = new Phone3();
        //new Thread(()->{phone31.sendMessage();},"A").start();
        //new Thread(()->{phone32.sendMessage();},"B").start();
        /*
            发短信-A
            打电话-B
         */
        //new Thread(()->{
        //    for (int i = 0; i < 100; i++) {
        //        phone31.sendMessage();
        //    }
        //},"A").start();
        //new Thread(()->{
        //    for (int i = 0; i < 100; i++) {
        //        phone32.sendMessage();
        //    }
        //},"B").start();
        /*
            ...
            发短信-A
            发短信-B
            发短信-A
            发短信-A
            ...
            为什么存在这种现象？既然静态锁的对象是类的模板，不应该是 发完消息，再打电话？
            猜测：
            会不会是执行完方法后就释放模板锁，在这个间隙，B线程获取到了模板锁，然后执行了？
            原因：
            参考文章：http://t.csdn.cn/K8e8e
                近期在使用多线程开发时遇到一些有意思的东西—在线程run方法中是否需要当前线程睡眠一段时间。
                要了解sleep方法，那么首先得了解到它的原理及其使用方法。
                使用简介：
                在这里以JAVA的API为例（当然其它语言亦可以），JAVA的API中是这么描述的，
                    public static void sleep(long millis,
                                         int nanos)
                                  throws InterruptedException
                在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠（暂停执行），
                此操作受到系统计时器和调度程序精度和准确性的影响。
                参数： millis - 以毫秒为单位的休眠时间。 nanos - 要休眠的另外 0-999999 纳秒。 抛出： IllegalArgumentException - 如果 millis 值为负或 nanos 值不在 0-999999 范围内。 InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时，当前线程的中断状态 被清除 API中说的很明确，这个方法的目的就是让线程休眠，并且这个操作其实是没有释放锁的。
                那么问题来了，sleep到底什么时候用呢？
                我们在使用多线程的时候会发现，有时候需要程序运行时间特别长了会经常出现一些问题，或者当前我们开启了多个线程它们分别执行几个任务，但是因为执行的任务时间非常短，有时候cpu切换时候会出现一系列的问题，那么这时候可能的原因就有是否因为cpu一直在执行一个线程或者其他的原因呢。
                当我们设置sleep时，等于告诉cpu，当前的线程不再运行，持有当前对象的锁。那么这个时候cpu就会切换到另外的线程了。这种操作有些时候是非常好的。
                那么回归标题，究竟应不应该使用sleep呢，LZ认为还是具体业务具体分析，看是否需要添加此方法。
         */

        //new Thread(()->{
        //    for (int i = 0; i < 100; i++) {
        //        phone31.sendMessage();
        //    }
        //},"A").start();
        //try {
        //    TimeUnit.SECONDS.sleep(5);  // 这里为什么要睡一下，才能打印出预想结果？
        //    // 猜测：
        //    // A调用方法的时候，持有锁，循环下一次的间隙中，CPU会想要切换执行线程B
        //    // 如果不加睡眠，线程B会在这个间隙获取锁、获取CPU执行，这样就会有问题，锁失去了意义
        //    // 所以，在想开启线程B的时候加个睡眠，等待A下次循环调用发消息方法时唤醒A线程，且让其获取锁，这样保证A继续持有CPU,避免B线程执行
        //    // A执行完后，睡5秒，5秒内A没有循环调用持有锁了，就开始执行线程B，B获取锁
        //    // 原因：
        //    // 因为锁在for循环里面，所以锁会被释放，可以把锁放在for循环外面，如Test2,这样就不会有这个问题了
        //} catch (InterruptedException e) {
        //    e.printStackTrace();
        //}
        //new Thread(()->{
        //    for (int i = 0; i < 100; i++) {
        //        phone32.sendMessage();
        //    }
        //},"B").start();


        //Q5:1对象分别调用static sychronized发消息, sychronized打电话，哪个先执行？
        //Phone4 phone4 = new Phone4();
        //new Thread(Phone4::sendMessage,"A").start();// 锁Class对象
        //new Thread(phone4::call,"B").start();// 锁调用者
        /*
            打电话-B    锁的不是一个东西，发消息睡眠1s，就先打印出打电话了
            发短信-A
         */


        //Q6:2对象分别调用static sychronized发消息, sychronized打电话，哪个先执行？
        Phone4 phone5 = new Phone4();
        Phone4 phone6 = new Phone4();
        new Thread(Phone4::sendMessage,"A").start();// 锁Class对象
        new Thread(phone6::call,"B").start();// 锁调用者
        /*
            打电话-B    锁的不是一个东西，发消息睡眠1s，就先打印出打电话了
            发短信-A
         */

    }
}

/*
    无锁
 */
class Phone1{
    public void sendMessage(){
        System.out.println("sendMessage");
    }
    public void call(){
        System.out.println("call");
    }
}

/*
    sychronized锁
        锁的对象是方法的调用者
 */
class Phone2{
    public synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信-"+Thread.currentThread().getName());
    }
    public synchronized void call(){
        System.out.println("打电话-"+Thread.currentThread().getName());
    }
    public void hello(){
        System.out.println("hello-"+Thread.currentThread().getName());
    }
}

/*
    static sychronized锁
        锁的对象是类的模板
        类一加载就有了Class对象
        Phone3只有唯一的Class对象  Class<Phone3> phone3Class = Phone3.class;
        sendMessage()和call()方法都被static修饰时，用的是同一个锁
 */
class Phone3{
    public static synchronized void sendMessage(){
        System.out.println("发短信-"+Thread.currentThread().getName());
    }
    public static synchronized void call(){
        System.out.println("打电话-"+Thread.currentThread().getName());
    }
}


/*
    1个static sychronized锁
    1个sychronized锁
 */
class Phone4{
    // 锁的Class类模板
    public static synchronized void sendMessage(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信-"+Thread.currentThread().getName());
    }
    // 锁的调用者
    public synchronized void call(){
        System.out.println("打电话-"+Thread.currentThread().getName());
    }
}
